﻿using System;
using System.Data;
using System.Text;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core.SqlServer
{
    /// <summary>Renders and executes update statements for SQL Server. Supports bulk update.</summary>
    sealed class SqlServerUpdater : IUpdater
    {
        /// <summary>Returns <b>true</b>. Bulk update is implemented for SQL Server.</summary>
        public bool ImplementsBulkUpdate
        {
            get { return true; }
        }

        #region RenderUpdate.

        /// <summary>Renders UPDATE statement.</summary>
        public void RenderUpdate(UpdateStatement update, DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
            if (update.UpdateList == null || update.UpdateList.Count == 0)
                throw new InvalidOperationException(Messages.SqlUpdater_SetListIsEmpty);

            output.Append("UPDATE ");
            update.Table.RenderTableName(dbms, output);
            AppendUpdateList(update.UpdateList, dbms, output, parameters);
            AppendRelations(update, dbms, output);
            AppendWhere(update.Where, dbms, output, parameters);
        }

        private static void AppendUpdateList(UpdateList list, DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
            output.Append(" SET ");
            for (int setExpressionIdx = 0; setExpressionIdx < list.Count - 1; setExpressionIdx++)
            {
                list[setExpressionIdx].Render(dbms, output, parameters);
                output.Append(", ");
            }

            list[list.Count - 1].Render(dbms, output, parameters);
        }

        private static void AppendRelations(UpdateStatement update, DbmsType dbms, StringBuilder output)        
        {
            if (update.Relations != null && update.Relations.Count > 0)
            {
                output.Append(" ");
                update.Relations.RenderFromClause(update.Table, dbms, output);
            }
        }

        private static void AppendWhere(SearchCondition where, DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
            bool hasFilter = (where != null) && !where.IsEmpty;
            if (hasFilter)
            {
                output.Append(" WHERE ");
                where.Render(dbms, output, parameters);
            }
        }

        #endregion

        #region Execute.

        /// <summary>Executes <see cref="UpdateStatement"/>.</summary>
        public int Execute(UpdateStatement update, DbmsType dbms, IConnectionProvider conn, out CommandExecutionStatistics lastExecutedCommandInfo)
        {
            DbParameterCollection parameters = new DbParameterCollection();
            StringBuilder cmdText = new StringBuilder();
            RenderUpdate(update, dbms, cmdText, parameters);

            string command = cmdText.ToString();
            lastExecutedCommandInfo = new CommandExecutionStatistics(command);
            int rowsAffected = DbUtil.ExecuteNonQuery(conn, command, parameters, CommandType.Text);
            lastExecutedCommandInfo.StopTime();
            return rowsAffected;
        }

        #endregion

        #region RenderSelect - not implemented.

        /// <summary>Not implemented. Does nothing because bulk update is supported for SQL Server.</summary>
        public void RenderSelect(UpdateStatement update, DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
        }

        #endregion
    }
}